#include "HY1C_out.h"
/* ========================================================================
 *  l2binmatch creates HDF file with L2 sensor validation data
 *  
 *       where
 * 
 *  L2 files/granules for a given time period from the sensor being validated
 *  L3 daily, 8-day, etc. bin file from the sensor used as validation truth
 *
 *  The program assumes that all L3 products are float!
 *
 *     Programmer     Organization      Date       Description of change
 *   --------------   ------------    --------     ---------------------
 *   Ewa Kwiatkowska   SAIC       13 January 2004    Original development
 *   Ewa Kwiatkowska   SAIC       20 October 2004    Introducing user defined list
 *						     of L2 and L3 products
 *						     All L2 and L3 products are float32
 *						     except for L2 l2_flags, mside, detnum, pixnum
 *   Ewa Kwiatkowska   SAIC       7 November 2007    Added nscenes, nobs product
 *   Joel Gales        Futurtech  3 November 2012    Add ds_id.fftype
 *
 * ======================================================================== */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <libgen.h>
#include <string.h>
#include <ctype.h>

#include "seabin.h"

#include "l12_proto.h"
#include "readL2scan.h"
#include "msl2val_struc.h"
#include "hdf.h"
#include <setupflags.h>

int32 read_attrs(int32 sdfid, meta_l3bType *meta_l3b);

#define MAXNVDATA 50
#define NBANDSVIS 6




int32 nrows;
int32 close_input_l3b(int32 fid, int32 sdfid, int32 vgid, int32 vdata_id[MAXNVDATA]);
int32 open_input_l3b(char *hdf_file, int32 *fid, int32 *sdfid, int32 *vgid, int32 vdata_id[MAXNVDATA], meta_l3bType *meta_l3b);
int   read_glbl_attr(int32 sd_id, char *name, VOIDP ptr);

int   msl2val_input_init(inputstr *input_str);
int   msl2val_input(int argc, char **argv, inputstr *input_str);
int   msl2val_usage (char *progname);
int   msl2val_get_item(char *arg, inputstr *input_str);
int   msl2val_par_file(char *pfile, inputstr *input_str);

int32 locate(int32 *binlist_buf, int32 n_records, int32 bin);
int   nLw_SeaWiFS2MODIS(int32_t sensorID, float *nLwbands);
int   read9km_mask(char *file, char *mask);
int   is_masked(int32_t bin, char *mask, int32 nbrows);

void  meanday(int32_t year1, int32_t day1, int32_t msec1, int32_t year2, int32_t day2, int32_t msec2, int32_t *myear, int32_t *mday, int32_t *mmsec);
int32 find_L2L3products(inputstr input, msvalstr *valstr);
int32 strproducts(char *prodstr, int32 *nprods, prname *l2prods);



int main (int argc, char **argv)
{
  int32   i,j,l,n,np;
  int32_t   iscan    = 0;          /* input scan number                       */
  int32_t   ipix;			/* input pixel number                      */
  int32_t   npixs;          	/* number of matched pixels in the L2 file */

  float32 latbin;
  float   lon, lat;
  int32   *numbin;
  int32   *basebin;
  int32   bin_row, bin_col, bin, ibin;
  
  int32   iprod;

  int32   nl2files, tl2files;
  l2_prod *l2_str;	         /* input data for l2 calibrator         */
  meta_l2Type *meta_l2;          /* input metadata for l2 calibrator     */
  float   nlws[NBANDSVIS];

  int     nl2prods, *l2prodinx, nl3prods, *l3prodinx, *l2finx, *inx;
  char    data_prodname[128], l2sensor[100];
  uint8   nlw_suite;
  
  inputstr input;
  int32_t     l2sensorID, l3sensorID, mdate[3];
  
  int32 n_records, interlace, interlace0, vdata_size;
  char  fields[60], vdata_name[H4_MAX_NC_NAME];
  char  l3_flags[500];
  int32 bin_num, flags, vsize;
  int16 obs, *nobs, scenes, *nscenes;
  float32 wgt, *fp32;
  
  int32 fid;
  int32 sdfid;
  int32 vgid;
  int32 vdata_id[MAXNVDATA];
  int   status;
  int32 binindex_buf;
  int32 nread;
  int32 nprod;
  off_t size;
  int32 sd_id;
  struct stat bufor;

  char  buf[5000], *mask, *maskfile, maskname[1000];

  float32 *sum_buf[MAXNVDATA-2], f32;
  uint8   *binlist_buf=NULL;
  int32   *bins;

  meta_l3bType   meta_l3b;
  uint32_t  flagusemask;
  uint32_t  required_mask;
  msvalstr       valstr;
  



  if (argc < 2) msl2val_usage(argv[0]);
  
  
  if (msl2val_input(argc, argv, &input) != 0) {
        HY1C_out("-E- %s: Input parameters cannot be read properly\n", argv[0]);
        exit(FATAL_ERROR);
  }
 
 
  /* Determine number of input files */
  /* ------------------------------- */
  nl2files = 0;
  for (i=0; i<MAX_IFILES; i++) if (input.il2files[i][0] != '\0') ++nl2files; else break;
  tl2files = nl2files;
  for (i=0; i<nl2files; i++) {
    	if (access(input.il2files[i], F_OK) || access(input.il2files[i], R_OK)) {
        	HY1C_out("-E- %s: Input file '%s' does not exist or cannot open.\n",
               		argv[0], input.il2files[i]);
        	exit(FATAL_ERROR);
	}
  }
  if (nl2files < 2) 
        HY1C_out("There is %d input l2 file\n", nl2files);
  else
        HY1C_out("There are %d input l2 files\n", nl2files);
  nl2prods = input.nl2prods;
  nl3prods = input.nl3prods;
  if ((l2prodinx = malloc(nl2prods*sizeof(int))) == NULL) {
        HY1C_out("-E- %s: Cannot allocate memory to l2prodinx\n", argv[0]);
        exit(FATAL_ERROR);
  }
  if ((l3prodinx = malloc(nl3prods*sizeof(int))) == NULL) {
        HY1C_out("-E- %s: Cannot allocate memory to l3prodinx\n", argv[0]);
        exit(FATAL_ERROR);
  }
  
  
  
  
  status = open_input_l3b(input.il3file, &fid, &sdfid, &vgid, vdata_id, &meta_l3b);
  if (status == FAIL) {
       exit(FATAL_ERROR);
  }


  /* Calculate the number of L3 products */
  /* ----------------------------------- */
  status = 0;
  nprod = MAXNVDATA-2;
  for (i=0; i<MAXNVDATA; i++) {
       if (vdata_id[i] == -1) nprod--;
  }

  if (nprod < nl3prods) {
       HY1C_out("-E- %s: There are too few products in the L3 file %s\n", argv[0], input.il3file);
       exit(FATAL_ERROR);
  }
     



  /* Make sure L3 product names are identical for every input L3 file */
  /* ---------------------------------------------------------------- */
  status = VSsetfields(vdata_id[0], "bin_num,nobs,nscenes,weights,flags_set");
  status = VSsetfields(vdata_id[1], "start_num,begin,extent,max");
  
  n = 0;
  l = 0;
  for (iprod=0; iprod<nprod; iprod++) {
   
      VSgetname(vdata_id[2+iprod], data_prodname);
      
      for (i=0; i<nl3prods; i++) if (strcmp(data_prodname, input.l3prods[i]) == 0) {
          if (strncmp(data_prodname, "nLw_", 4) == 0) l++;
          l3prodinx[i] = iprod;
	  strcpy(buf, input.l3prods[i]);
	  strcat(buf, "_sum,");
	  strcat(buf, input.l3prods[i]);
	  strcat(buf, "_sum_sq");
	  status = VSsetfields(vdata_id[2+iprod], buf);
	  n++;
      	  if (status != 0) {
	     HY1C_out("-E- %s: Failed to set field for %s in %s\n", argv[0], input.l3prods[i], input.il3file); 
	     exit(FATAL_ERROR);
	  }
      }
  }
  
  if ( n != nl3prods ) {
       HY1C_out("-E- %s: Some L3 products were not found in %s\n", argv[0], input.il3file); 
       exit(FATAL_ERROR);
  }
      
  if ( l >= NBANDSVIS ) nlw_suite = 1; else nlw_suite = 0;
  
  
 
  VSinquire(vdata_id[2+l3prodinx[0]], &n_records, &interlace, fields, &vdata_size, vdata_name);
  
  VSinquire(vdata_id[0], &n, &interlace0, fields, &vdata_size, vdata_name);
  if (n != n_records) {
   	HY1C_out("-E- %s: Different number of records for the product %s\n", argv[0], vdata_name);
        exit(FATAL_ERROR);
  }
  for (iprod=1; iprod<nl3prods; iprod++) {
       VSinquire(vdata_id[2+l3prodinx[iprod]], &n, &interlace, fields, &vdata_size, vdata_name);
       if (n != n_records) {
       	   HY1C_out("-E- %s: Different number of records for the product %s\n", argv[0], vdata_name);
	   exit(FATAL_ERROR);
       }
  }
  
  
  
  for (iprod=0; iprod<nl3prods; iprod++) {
        if ( (sum_buf[iprod] = (float32 *) calloc(2*n_records, sizeof(float32))) == NULL ) {
            HY1C_out("-E- %s: Error allocating memory to L3 file products\n",argv[0]);
            exit(FATAL_ERROR);
	}
  } /* iprod loop */
  vsize = 2*sizeof(int32)+sizeof(float32)+2*sizeof(int16);
  if ( (binlist_buf = (uint8 *) calloc(n_records, vsize)) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory L3 file bin info\n",argv[0]);
        exit(FATAL_ERROR);
  }
	
  binindex_buf = n_records;

  /* Get bin numbers for each filled bin */
  /* ----------------------------------- */
  nread = VSread(vdata_id[0], (uint8 *) binlist_buf, binindex_buf, interlace0);

  if (nread == -1) {
      HY1C_out("-E- %s: Unable to read bin numbers...: %d\n", argv[0], binindex_buf);
      exit(FATAL_ERROR);
  }

  /* error check */
  if (binindex_buf != n_records) {
      HY1C_out("-E- %s: Wrong number of records were read: %d\n", argv[0], binindex_buf);
      exit(FATAL_ERROR);
  }
  
  /* Get data values (sum) for each filled bin */
  /* ----------------------------------------- */
  for (iprod=0; iprod<nl3prods; iprod++) {
  

      VSseek(vdata_id[2+l3prodinx[iprod]], 0);
      
      nread = VSread(vdata_id[2+l3prodinx[iprod]], (uint8 *) sum_buf[iprod], binindex_buf, interlace);

      if (nread == -1) {
	  HY1C_out("-E- %s: Unable to read sum/sum_sq: %d\n", argv[0], binindex_buf);
          exit(FATAL_ERROR);
      }
      
       /* error check */
      if (binindex_buf != n_records) {
      	  HY1C_out("-E- %s: Wrong number of records were read: %d\n", argv[0], binindex_buf);
          exit(FATAL_ERROR);
      }
      
  } /* iprod loop */
  
  
  close_input_l3b(fid, sdfid, vgid, vdata_id);
  free(l3prodinx);
  
 
  if ( (bins = (int32 *) malloc(n_records * sizeof(int32))) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory to L3 file bins\n",argv[0]);
        exit(FATAL_ERROR);
  }
  if ( (nscenes = (int16 *) malloc(n_records * sizeof(int16))) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory to L3 file scenes\n",argv[0]);
        exit(FATAL_ERROR);
  }
  if ( (nobs = (int16 *) malloc(n_records * sizeof(int16))) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory to L3 file obs\n",argv[0]);
        exit(FATAL_ERROR);
  }
      
  l3_flags[0] = '\0';
  for (i=0; i<NFLAGS; i++) {
       strcat(l3_flags, l2_flag_lname[i]);
       if (i<NFLAGS-1) strcat(l3_flags, ",\0");
  }
  setupflags(l3_flags, input.l3flaguse, &flagusemask, &required_mask, &status );
  if (status < 0) {
       HY1C_out("-E- %s: Error setting up flags for L3 data.\n",argv[0]);
       exit(FATAL_ERROR);
  }
  
  if (input.deepwater > 0) {
        if ( (mask = (char *) malloc(5940424 * sizeof(char))) == NULL ) {
            HY1C_out("-E- %s: Error allocating memory to the mask data\n", argv[0]);
            exit(FATAL_ERROR);
	}
        if ((maskfile = getenv("OCDATAROOT")) == NULL) {
            HY1C_out("-E- %s: OCDATAROOT directory undefined.\n", argv[0]);
            exit(FATAL_ERROR);
        }
	strcpy(maskname, maskfile);
	strcat(maskname, "/common/deep_water_mask_9km.dat\x0");
	
	if (read9km_mask(maskname, mask) > 0) {
            HY1C_out("-E- %s: Failed reading the deep water mask file\n", argv[0]);
            exit(FATAL_ERROR);
        }
  }
  
  
  n = 0;
  for (i=0; i<n_records; i++) {
      
      j = 1; 
      memcpy((void *)&bin_num, (const void *)&binlist_buf[i*vsize], 4);
      memcpy((void *)&obs, (const void *)&binlist_buf[i*vsize+4], 2);
      memcpy((void *)&scenes, (const void *)&binlist_buf[i*vsize+6], 2);
      memcpy((void *)&wgt, (const void *)&binlist_buf[i*vsize+8], 4);
      memcpy((void *)&flags, (const void *)&binlist_buf[i*vsize+12], 4);
      memcpy((void *)&f32, (const void *)&sum_buf[0][2*i], 4);
      if (input.deepwater > 0) j = (int32_t)is_masked(bin_num, mask, nrows);
      if (obs > 0 && wgt > 0.0 && ((flags & flagusemask) == 0) && f32 > 0.0 && j > 0) {
   	  for (iprod=0; iprod<nl3prods; iprod++) sum_buf[iprod][n] = sum_buf[iprod][2*i]/wgt;
	  bins[n] = bin_num;
	  nobs[n] = obs;
	  nscenes[n++] = scenes;
      }
  }
 
  if (input.deepwater > 0) {
  	free(mask);
	j = (int32_t)is_masked(bin_num, mask, -1);
  }
  free(binlist_buf);
  
  if (n <= 0) {
       HY1C_out("-E- %s: No valid L3 ocean data for given flags %s\n", argv[0], input.il3file);
       for (iprod=0; iprod<nl3prods; iprod++) free(sum_buf[iprod]);
       free(bins);
       exit(NOMATCH_ERROR);
  }
  
  for (iprod=0; iprod<nl3prods; iprod++) {
       if ( (sum_buf[iprod] = (float32 *)realloc((void *)sum_buf[iprod], n*sizeof(float32))) == NULL ) {
             HY1C_out("-E- %s: Error reallocating memory to L3 file products\n",argv[0]);
             exit(FATAL_ERROR);
       }
  } /* iprod loop */
  
  if (n < n_records) {
       n_records = n;
       if ((bins = (int32 *)realloc((void *)bins, n_records*sizeof(int32))) == NULL ) {
            HY1C_out("-E- %s: Error reallocating memory to L3 file bins\n",argv[0]);
            exit(FATAL_ERROR);
       }
       if ((nscenes = (int16 *)realloc((void *)nscenes, n_records*sizeof(int16))) == NULL ) {
            HY1C_out("-E- %s: Error reallocating memory to L3 file scenes\n",argv[0]);
            exit(FATAL_ERROR);
       }
       if ((nobs = (int16 *)realloc((void *)nobs, n_records*sizeof(int16))) == NULL ) {
            HY1C_out("-E- %s: Error reallocating memory to L3 file obs\n",argv[0]);
            exit(FATAL_ERROR);
       }
  }
     
	         
  
  
  
  if ( (l2_str = (l2_prod *)malloc(nl2files*sizeof(l2_prod))) == NULL ) {
       HY1C_out("-E- %s: Error allocating memory for reading L2 data.\n",argv[0]);
       exit(FATAL_ERROR);
  }
  for (i=0; i<nl2files; i++) {
       l2_str[i].nrec = 0;
       l2_str[i].nsamp = 0;
       HY1C_out("Opening %d %s\n",i,input.il2files[i]);
       if ( openL2(input.il2files[i], 0x0, &l2_str[i]) ) {
            HY1C_out("-E- %s: Error reading L2 data %s.\n", argv[0], input.il2files[i]);
            exit(FATAL_ERROR);
       }
  }
  
  /* Make sure numbers of L2 products are identical for every input L2 file */
  /* ---------------------------------------------------------------------- */
  status = 0;
  for (i=1; i<nl2files; i++)
  if (l2_str[i-1].nprod != l2_str[i].nprod) {
	HY1C_out("-E- %s: Number of products for %s (%d) differs from %s (%d)\n",argv[0],
	 l2_str[i-1].filename, l2_str[i-1].nprod, l2_str[i].filename, l2_str[i].nprod);
	status = -1;
  }
  if (status == -1) exit(FATAL_ERROR);


  /* Make sure L2 product names are identical for every input L2 file */
  /* ---------------------------------------------------------------- */
  status = 0;
  for (i=1; i<nl2files; i++)
     for (j=0; j<l2_str[i].nprod; j++)
      	if (strcmp(l2_str[i-1].prodname[j], l2_str[i].prodname[j]) != 0) {
		HY1C_out("-E- %s: Product %d for %s (%s) differs from %s (%s)\n",argv[0],
	        j, l2_str[i-1].filename, l2_str[i-1].prodname[j], l2_str[i].filename, l2_str[i].prodname[j]);
		status = -1;
    	}
  if (status == -1) exit(FATAL_ERROR);

  
 
  /* Make sure all L2 products required for the validation are present */
  /* ----------------------------------------------------------------- */

  n = 0;
  l = 0;
  while (n < nl2prods) {
  
    l2prodinx[n] = -2;
    if (strcmp(input.l2prods[n], "l2_flags") == 0) {
	l2prodinx[n] = -1;
        for (i=0; i<nl2files; i++) if (l2_str[i].l2_flags == NULL) l2prodinx[n] = -2;
    } else
    if (strcmp(input.l2prods[n], "mside") == 0) {
	l2prodinx[n] = -1;
        for (i=0; i<nl2files; i++) if (l2_str[i].mside == NULL) l2prodinx[n] = -2;
    } else
    if (strcmp(input.l2prods[n], "detnum") == 0) {
	l2prodinx[n] = -1;
        for (i=0; i<nl2files; i++) if (l2_str[i].detnum == NULL) l2prodinx[n] = -2;
    } else
    if (strcmp(input.l2prods[n], "pixnum") == 0) {
	l2prodinx[n] = -1;
        for (i=0; i<nl2files; i++) if (l2_str[i].pixnum == NULL) l2prodinx[n] = -2;
    } else
    for (i=0; i<l2_str[0].nprod; i++) {
       	if (strcmp(input.l2prods[n], l2_str[0].prodname[i]) == 0) {
	    if (strncmp(input.l2prods[n], "nLw_", 4) == 0) l++;
	    l2prodinx[n] = i;
	    break;
	}
    }

    if (l2prodinx[n] < -1) {
        HY1C_out("-E- %s: %s product is missing from the L2 data file %s.\n", argv[0], input.l2prods[n], l2_str[0].filename);
        exit(FATAL_ERROR);
    }
    n++;
  }
  
  if (l >= NBANDSVIS && nlw_suite == 1) nlw_suite = 1; else nlw_suite = 0;
  

  
  if ( (l2finx = (int *)malloc(nl2files*sizeof(int))) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory for L2 file index.\n",argv[0]);
        exit(FATAL_ERROR);
  }
  if ( (meta_l2 = (meta_l2Type *)malloc(nl2files*sizeof(meta_l2Type))) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory for L2 metadata.\n",argv[0]);
        exit(FATAL_ERROR);
  }
	    

  n = 0;
  for (i=0; i<nl2files; i++)  {
    
       l2finx[i] = 1;

       if ( readL2meta(&(meta_l2[i]), i) ) {
            HY1C_out("-E- %s: Error reading L2 metadata %s.\n",argv[0], input.il2files[i]);
            exit(FATAL_ERROR);
       }

       /* grab the target sensor name from the L2 meta-data */
       if ( i==0 ) 
            strcpy(l2sensor, meta_l2[i].sensor_name); 
       else if (strcmp(l2sensor, meta_l2[i].sensor_name) != 0) {
            HY1C_out("-E- %s: Mixing L2 data from different sensors, %s and %s.\n",
                 argv[0], l2sensor, meta_l2[i].sensor_name);
	    exit(FATAL_ERROR);
       }
	   
       /* make sure L2 scene has some ocean data */
       /*
      if (meta_l2[i].flags_pc[NFLAGS-1] < 0.04) {
	    l2finx[i] = 0;
	    HY1C_out("-W- %s: L2 file %s does not contain valid ocean data\n\n",
                 argv[0], input.il2files[i]);
      }
       */
	   
      for (j=0; j<i; j++) {
	    if (l2_str[i].syear == l2_str[j].syear && l2_str[i].sday ==
	     l2_str[j].sday && l2_str[i].smsec == l2_str[j].smsec &&
	     l2_str[i].eyear == l2_str[j].eyear && l2_str[i].eday ==
	     l2_str[j].eday && l2_str[i].emsec == l2_str[j].emsec) {
	     	  HY1C_out("-W- %s: L2 coverage is repeated between the files %s and %s\n\n", 
		  argv[0], input.il2files[i], input.il2files[j]);
		  l2finx[i] = 0;
	     }
      }

      if (l2_str[i].syear*1000+l2_str[i].sday >= meta_l3b.syear*1000+meta_l3b.sday && 
       l2_str[i].eyear*1000+l2_str[i].eday <= meta_l3b.eyear*1000+meta_l3b.eday)
       ;
      else {
       	     HY1C_out("-W- %s: L2 file start-date %d %d and end-date %d %d outside of L3 file coverage %d %d - %d %d\n",argv[0],l2_str[i].syear,l2_str[i].sday,l2_str[i].eyear,l2_str[i].eday,meta_l3b.syear,meta_l3b.sday,meta_l3b.eyear,meta_l3b.eday);
             l2finx[i] = 0;
      }
      
      n += l2finx[i];

  }
   
  /*					 	 */
  /* if no valid L2 files to target bail         */
  /*					 	 */
  if ( n < 1 ) {
	
       HY1C_out("-E- %s: No valid L2 ocean data within the L3 daily %s\n", argv[0], input.il3file);
       for (i=0; i<nl2files; i++) {
	    closeL2(&l2_str[i], i);
	    freeL2meta(&meta_l2[i]);
	    freeL2(&l2_str[i]);
       }
       freeL2(NULL);
       free(l2_str);
       free(meta_l2);
       free(l2finx);
       for (iprod=0; iprod<nl3prods; iprod++) free(sum_buf[iprod]);
       free(l2prodinx);
       free(bins);
       free(nobs);
       free(nscenes);
	    
       exit(NOMATCH_ERROR);
  }
      
  /*					 			    */
  /* Free some memory if not all L2 files overlap the L3 bins       */
  /*					 			    */
  if ( n < nl2files ) {
	
    	if ( (inx = (int *)malloc(n*sizeof(int))) == NULL ) {
            HY1C_out("-E- %s: Error allocating memory for L2 file index.\n",argv[0]);
            exit(FATAL_ERROR);
    	}
	j = 0;
	for (i=0; i<nl2files; i++) 
	    if (l2finx[i]) {
	         inx[j++] = i;
	    }
	    else {
	    	 freeL2(&l2_str[i]);
    	    }
		
	nl2files = n;
  }
  else {
	
    	if ( (inx = (int *)malloc(nl2files*sizeof(int))) == NULL ) {
            HY1C_out("-E- %s: Error allocating memory for L2 file index.\n",argv[0]);
            exit(FATAL_ERROR);
    	}
	for (i=0; i<nl2files; i++) inx[i] = i;
  }
   
  free(l2finx);
  
   
   
  for (i=0; i<SENSOR_NUM; i++)
       if (strcmp(l2sensor, sensorName[i]) == 0 ) {
           l2sensorID = i;
	   break;
       }
  for (i=0; i<SENSOR_NUM; i++)
       if (strcmp(meta_l3b.sensor_name, sensorName[i]) == 0 ) {
           l3sensorID = i;
	   break;
       }
  
  

  /* ----------------- */
  /* Set up bin arrays */
  /* ----------------- */
  if ( (numbin   = (int32 *)   calloc(nrows, sizeof(int32))) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory to numbin\n",argv[0]);
        exit(FATAL_ERROR);
  }
  if ( (basebin  = (int32 *)   calloc(nrows+1, sizeof(int32))) == NULL ) {
        HY1C_out("-E- %s: Error allocating memory to basebin\n",argv[0]);
        exit(FATAL_ERROR);
  }

  bin = 0;
  for (i=0; i<nrows; i++) {
        latbin = (i + 0.5) * (180.0 / nrows) - 90.0;
        numbin[i] = (int32) (cos(latbin * PI/ 180.0) * (2.0*nrows) + 0.5);
        bin += numbin[i];
  }
  HY1C_out("Total number of possible bins in the L3 file: %d\n", bin);
  
  HY1C_out("Number of valid data bins in the L3 file: %d\n", n_records);

  basebin[0] = 1;
  for (i=1; i<=nrows; i++) {
        basebin[i] = basebin[i-1] + numbin[i-1];
  }



  valstr.l2sensorID = l2sensorID;
  valstr.nl2prods = nl2prods;
  valstr.l3sensorID = l3sensorID;
  valstr.nl3prods = nl3prods;
 
  j = find_L2L3products(input, &valstr);
  
  if (nlw_suite && (l2sensorID == MODISA || l2sensorID == MODIST) && l3sensorID == SEAWIFS && input.bpass_corr) nlw_suite = 1; else nlw_suite = 0;
  

   
   
  for (i=0; i<nl2files; i++)  {
  
	
        n = l2_str[inx[i]].nrec*l2_str[inx[i]].nsamp;
	
	l = alloc_valstr(1, n, &valstr);
    
	strcpy(valstr.filenames[0], input.il2files[inx[i]]);
	
	npixs = 0;
	np = 0;
	
	
        /* Setup flag mask */
        /* --------------- */
        strcpy(buf, l2_str[inx[i]].flagnames);
        setupflags(buf, input.l2flaguse, &flagusemask, &required_mask, &status );
        if (status < 0) {
             HY1C_out("-E- %s: Error reading L2 flags %s.\n",argv[0], input.il2files[inx[i]]);
             exit(FATAL_ERROR);
        }
	
	
        for (iscan = 0; iscan < l2_str[inx[i]].nrec; iscan++) {
      
             if ((iscan % 1000) == 0) HY1C_out("Processing file %d out of %d files, scan %d\n", i+1, nl2files, iscan);
	     
	     if (readL2(&(l2_str[inx[i]]), inx[i], iscan, -1, NULL)) {
                   HY1C_out("%s: Error reading L2 data file %s at scan %d.\n",
                       argv[0], l2_str[inx[i]].filename, iscan);
                   continue;
      	     }
		
	     if (iscan == 0) meanday((int32_t)l2_str[inx[i]].syear, (int32_t)l2_str[inx[i]].sday, l2_str[inx[i]].smsec, (int32_t)l2_str[inx[i]].eyear, (int32_t)l2_str[inx[i]].eday, l2_str[inx[i]].emsec, &(mdate[0]), &(mdate[1]), &(mdate[2]));

      	     for (ipix = 0; ipix < l2_str[inx[i]].nsamp; ipix++) 
	     
	           if ((l2_str[inx[i]].l2_flags[ipix] & flagusemask) == 0) {
		
	               lat = l2_str[inx[i]].latitude[ipix];
	               lon = l2_str[inx[i]].longitude[ipix];
		   		      
	  	       bin_row = (int32_t) ((90.0 + lat) * ((float64) nrows / (float64) 180.0));
	  	       bin_col = (int32_t) ((float64) numbin[bin_row] * (lon + 180.0) / (float64) 360.0);
	  	       bin = basebin[bin_row] + bin_col;
		       
		       ibin = locate(bins, n_records, bin);
		       if (ibin >= 0) {
		       
			   np++;
		           if (np % input.subsmpl) continue;
			   
			   if (nlw_suite) {
			       l = 0;
			       for (j=0; j<valstr.nl3prods; j++) {
			           if (strncmp(valstr.l3prods[j], "nLw", 3) == 0) nlws[l++] = (float)sum_buf[j][ibin];
			   	   if (l == NBANDSVIS) break;
			       }
			       status = nLw_SeaWiFS2MODIS(l2sensorID, nlws);
		       	       if (status == 1) continue;
			   }
			       
	    	    	   valstr.fileID[npixs] = (int16)0;
	    	    	   valstr.year[npixs] = (int16)mdate[0];
	    	    	   valstr.day[npixs] = (int16)mdate[1];
	    	    	   valstr.msec[npixs] = (int32)mdate[2];
	    	    	   valstr.lon[npixs] = lon;
	    	    	   valstr.lat[npixs] = lat;
	    	    	   valstr.iscan[npixs] = (int16)(iscan);
	    	    	   valstr.nobs[npixs]  = nobs[ibin];
	    	    	   valstr.nscenes[npixs] = nscenes[ibin];
			   
			   l = 0;
			   for (j=0; j<valstr.nl3prods; j++) {
			   
			       	if (nlw_suite && (strncmp(valstr.l3prods[j], "nLw", 3) == 0) && l < NBANDSVIS)
			           valstr.data_vic[j*n+npixs] = nlws[l++];
				else 
				   valstr.data_vic[j*n+npixs] = (float32)sum_buf[j][ibin];
			   }
			   
			   l = 0;
			   for (j=0; j<valstr.nl2prods; j++) {
			   
				if (strcmp(valstr.l2prods[j], "l2_flags") == 0) {
			   	     valstr.l2_flags[npixs] = (int32)l2_str[inx[i]].l2_flags[ipix];
				} else
    				if (strcmp(valstr.l2prods[j], "mside") == 0) {
				     valstr.mside[npixs] = (uint8)(l2_str[inx[i]].mside[iscan]);
				} else
    				if (strcmp(valstr.l2prods[j], "detnum") == 0) {
				     valstr.detnum[npixs] = (uint8)(l2_str[inx[i]].detnum[iscan]);
				} else
    				if (strcmp(valstr.l2prods[j], "pixnum") == 0) {
				     valstr.pixnum[npixs] = (int16)(l2_str[inx[i]].pixnum[ipix]);
				} else {
				     valstr.data_val[l*n+npixs] = (float32)(l2_str[inx[i]].l2_data[l2prodinx[j]*l2_str[inx[i]].nsamp+ipix]);
				     ++l;
      	     			}

			   }
			
			   npixs++;
		       }
		   }
	}
	

	if (npixs > 0) {	   
   
	    for (j=1; j<valstr.nl3prods; j++) 
	        if ((fp32 = (float32 *)memmove((void *)&(valstr.data_vic[j*npixs]), (const void *)&(valstr.data_vic[j*n]), sizeof(float32)*npixs)) == NULL) {
	            HY1C_out("-E- %s: Problem reallocating memory to data_vic\n", argv[0]);
	            exit(FATAL_ERROR);
	    	}

	    l = 0;
	    for (j=0; j<valstr.nl2prods; j++) {
		if (strcmp(valstr.l2prods[j], "l2_flags") == 0) ;
		else
    		if (strcmp(valstr.l2prods[j], "mside") == 0) ;
		else
    		if (strcmp(valstr.l2prods[j], "detnum") == 0) ;
		else
    		if (strcmp(valstr.l2prods[j], "pixnum") == 0) ;
		else {
		    if (l > 0) {
		    	if ((fp32 = (float32 *)memmove((void *)&(valstr.data_val[l*npixs]), (const void *)&(valstr.data_val[l*n]), sizeof(float32)*npixs)) == NULL) {
	        	     HY1C_out("-E- %s: Problem reallocating memory to data_val\n", argv[0]);
	        	     exit(FATAL_ERROR);
		    	}
	            }
		    ++l;
	        }
	    }

	    valstr.npixs = npixs;

    	    if (msval_append(input.ofile, valstr) != LIFE_IS_GOOD) {
	        HY1C_out("-E- %s: Problem writing to the HDF file %s\n", argv[0], input.ofile);
	        exit(FATAL_ERROR);
	    }
    
    	    sd_id = SDstart(input.ofile, DFACC_RDONLY);
    	    if (sd_id == FAIL) {
		size = 0;
    	    }
    	    else {
		SDend(sd_id);
		if (stat((const char *)input.ofile, &bufor) != 0) {
	    	    HY1C_out("%s Error: Problem checking the status of the HDF file %s\n", argv[0], input.ofile);
	    	    exit(FATAL_ERROR);
    		}
		size = bufor.st_size;
    	    }
	    	    
	    if (size >= 2000000000L) {
	    	HY1C_out("%s Error: Size of the validation HDF output file exceeded 2GB\n", argv[0]);
		HY1C_out("   Files starting from the %d file (%s) will not be appended\n", i+1, input.il2files[inx[i]]);
	    	exit(FILESIZE_ERROR);
    	    }	    
	    
    	} else {
	    HY1C_out("-W- %s: No validation pixel data found in the L2 file %s\n", argv[0], input.il2files[inx[i]]);
	}
	    	  
	if (i == nl2files-1) free_valstr(valstr, 1); else free_valstr(valstr, 0);
    
  }
  
  	
  for (i=0; i<tl2files; i++) {
	 closeL2(&l2_str[i], i);
	 freeL2meta(&meta_l2[i]);
  }
  for (i=0; i<nl2files; i++) freeL2(&l2_str[inx[i]]);
  freeL2(NULL);
  free(l2_str);
  free(meta_l2);
  free(inx);
  for (iprod=0; iprod<nl3prods; iprod++) free(sum_buf[iprod]);
  free(bins);
  free(nscenes);
  free(nobs);
  free(numbin);
  free(basebin);
  free(l2prodinx);
  
   
  return SUCCEED;
      
 
}





int32 open_input_l3b(char *hdf_file, int32 *fid, int32 *sdfid, 
		    int32 *vgid, int32 vdata_id[MAXNVDATA], meta_l3bType *meta_l3b)
{
  intn i, j, status;
  int32 vg_ref;
  int32 tag;
  int32 ref;
  int32 vdid;
  char  nam_buf[80];
  char  cls_buf[80];

  if ((*fid = Hopen(hdf_file, DFACC_RDONLY, 0)) < 0)
    {
      HY1C_out("-E- %s line %d: Cannot open input HDF file on Hopen - %s\n", __FILE__,__LINE__, hdf_file);
      return FAIL;
    }

  Vstart(*fid);

  if ((*sdfid = SDstart(hdf_file, DFACC_RDONLY)) < 0)
    {
      HY1C_out("-E- %s line %d: Cannot open input HDF file on SDstart- %s\n", __FILE__,__LINE__, hdf_file);
      return FAIL;
    }


  read_attrs(*sdfid, meta_l3b);

  *vgid = -1;
  for (i=0; i<MAXNVDATA; i++) {
    vdata_id[i] = -1;
  }
  
  vg_ref = Vfind(*fid, "Level-3 Binned Data"); 
  if (vg_ref > 0) {
    *vgid = Vattach(*fid, vg_ref, "r");

    status = HXsetdir(dirname(hdf_file));
    
    j = 0;
    for (i=0; i<Vntagrefs(*vgid); i++) {
      status = Vgettagref(*vgid, i, &tag, &ref);
      vdid = VSattach(*fid, ref, "r");

      if (vdid == -1) {
	HY1C_out("-E- %s line %d: Problem opening Vdata (reference # %d)\n", __FILE__,__LINE__, ref);
	return FAIL;
      }

      VSgetname(vdid, nam_buf);
      VSgetclass(vdid, cls_buf);


      if (strcmp(cls_buf, "DataMain") == 0) {
	vdata_id[0] = vdid;
      }

      if (strcmp(cls_buf, "Index") == 0) {
	vdata_id[1] = vdid;
	nrows = VSelts(vdata_id[1]);
      }

      if (strcmp(cls_buf, "DataSubordinate") == 0) {
	vdata_id[2+j++] = vdid;
      }
    }
  }


  return SUCCEED;
}



int32 close_input_l3b(int32 fid, int32 sdfid, int32 vgid, int32 vdata_id[MAXNVDATA])
{

  intn i;

  if (vgid != -1) {
      Vdetach(vgid);

      for (i=0; i<MAXNVDATA; i++) {
	if (vdata_id[i] != -1) VSdetach(vdata_id[i]);
      }
  }

  SDend(sdfid);
  Vend(fid);
  Hclose(fid);


  return SUCCEED;
}

   






int msl2val_input_init(inputstr *input_str)
{
  int   i;
  char  l2iprods[] = "chlor_a,nLw_412,nLw_443,nLw_488,nLw_531,nLw_551,nLw_667,taua_869,epsilon,solz,sola,senz,sena,polcor_412,polcor_869,dpol_412,dpol_869,glint_coef,cloud_albedo,l2_flags,mside,detnum,pixnum";
  char  l3iprods[] = "chlor_a,nLw_412,nLw_443,nLw_488,nLw_531,nLw_551,nLw_667,taua_869,epsilon";
  int32 nprods;

  for (i=0; i<MAX_IFILES; i++) 
    input_str->il2files[i][0] = '\0';

  input_str->il3file    [0] = '\0';
  input_str->ofile      [0] = '\0';
  input_str->nl2prods = 23;
  i = strproducts(l2iprods, &nprods, (prname *)input_str->l2prods);
  if (nprods != input_str->nl2prods) {
      HY1C_out("-E- %s line %d: Wrong number of products defined and read\n", __FILE__,__LINE__);
      return FAIL;
  }
  input_str->nl3prods = 9;
  i = strproducts(l3iprods, &nprods, (prname *)input_str->l3prods);
  if (nprods != input_str->nl3prods) {
      HY1C_out("-E- %s line %d: Wrong number of products defined and read\n", __FILE__,__LINE__);
      return FAIL;
  }
  input_str->input_parms[0] = '\0';
  strcpy(input_str->l2flaguse, "ATMFAIL,LAND,HIGLINT,HILT,STRAYLIGHT,CLDICE,COCCOLITH,LOWLW,CHLFAIL,NAVWARN,ABSAER,MAXAERITER,ATMWARN,HISOLZEN,NAVFAIL,TURBIDW");
  strcpy(input_str->l3flaguse, "ATMFAIL,LAND,HIGLINT,HILT,HISATZEN,STRAYLIGHT,CLDICE,COCCOLITH,LOWLW,CHLFAIL,NAVWARN,ABSAER,MAXAERITER,ATMWARN,HISOLZEN,NAVFAIL,TURBIDW,MODGLINT");
  input_str->deepwater = 1;
  input_str->bpass_corr = 0;
  input_str->subsmpl = 1;
  input_str->evalmask = 0;

  return 0;
}




int msl2val_input(int argc, char **argv, inputstr *input_str)
{
  int    i, iret;
  char   buf[3000];
  int32  dim_sizes[H4_MAX_VAR_DIMS];
  int32  sd_id, numBands;
  intn   status;
  float  gain[NBANDS], offset[NBANDS];
  
  
  
  /*                                                                  */
  /* Set input values to defaults                                     */
  /*                                                                  */
  if ( msl2val_input_init( input_str ) != 0 ) {
      HY1C_out("-E- %s Line %d: Error initializing input structure.\n",__FILE__,__LINE__);
      return(-1);
  }

  /*                                                                  */
  /* Loop through command arguments and update input struct           */
  /*                                                                  */
  for (i=1; i<argc; i++) {

    iret = msl2val_get_item(argv[i], input_str);
    if (iret != 0) {
      HY1C_out("-E- %s Line %d: Error reading input parameters.\n", __FILE__,__LINE__);
      return(-1);
    }
  }
  

  /*                                                                  */
  /* Build string of input parameters for meta-data documentation     */
  /*                                                                  */

  if (input_str->il3file[0] != '\0') {
    sprintf(buf, "IL3FILE = %s", input_str->il3file);
    strcat(input_str->input_parms, buf);
    strcat(input_str->input_parms, "\n");
  }

  if (input_str->ofile[0] != '\0') {
    sprintf(buf, "OFILE = %s", input_str->ofile);
    strcat(input_str->input_parms, buf);
    strcat(input_str->input_parms, "\n");
  }

  if (input_str->l2prods != NULL) {
    sprintf(buf, "L2PRODS =  %s", input_str->l2prods[0]);
    strcat(input_str->input_parms, buf);
    for (i=1; i<input_str->nl2prods; i++) {
        sprintf(buf, ",%s", input_str->l2prods[i]);
        strcat(input_str->input_parms, buf);
    } 
    strcat(input_str->input_parms, "\n");
  }

  if (input_str->l3prods != NULL) {
    sprintf(buf, "L3PRODS =  %s", input_str->l2prods[0]);
    strcat(input_str->input_parms, buf);
    for (i=1; i<input_str->nl2prods; i++) {
        sprintf(buf, ",%s", input_str->l2prods[i]);
        strcat(input_str->input_parms, buf);
    } 
    strcat(input_str->input_parms, "\n");
  }
  
  sd_id = SDstart(input_str->il2files[0], DFACC_RDONLY);
  PTB( rdSDS(sd_id,"vcal_gain\0",0,0,0,0,(VOIDP)&gain) );
  PTB( rdSDS(sd_id,"vcal_offset\0",0,0,0,0,(VOIDP)&offset) );
  if (getDims(sd_id, "vcal_gain\0", dim_sizes) != 0) {
       	HY1C_out("-E- %s line %d: Error reading band number in %s.\n",__FILE__,__LINE__,input_str->il2files[0]);
        status = SDend(sd_id);
       	return(HDF_FUNCTION_ERROR);
  }
  status = SDend(sd_id);
  numBands = dim_sizes[0];

  sprintf(buf, "L2GAIN = %8.4f", gain[0]);
  strcat(input_str->input_parms, buf);
  for (i=1; i<numBands; i++) {
      sprintf(buf,", %8.4f", gain[i]);
      strcat(input_str->input_parms, buf);
  }
  strcat(input_str->input_parms, "\n");

  sprintf(buf, "L2OFFSET = %8.5f",offset[0]);
  strcat(input_str->input_parms, buf);
  for (i=1; i<numBands; i++) {
      sprintf(buf,", %8.5f", offset[i]);
      strcat(input_str->input_parms, buf);
  }
  strcat(input_str->input_parms, "\n");
    
  sprintf(buf, "L2FLAGUSE = %s", input_str->l2flaguse);
  strcat(input_str->input_parms, buf);
  strcat(input_str->input_parms, "\n");
  
  sprintf(buf, "L3FLAGUSE = %s", input_str->l3flaguse);
  strcat(input_str->input_parms, buf);
  strcat(input_str->input_parms, "\n");
  
  sprintf(buf, "DEEPWATER = %d", input_str->deepwater);
  strcat(input_str->input_parms, buf);
  strcat(input_str->input_parms, "\n");
  
  sprintf(buf, "BPASS_CORR = %d", input_str->bpass_corr);
  strcat(input_str->input_parms, buf);
  strcat(input_str->input_parms, "\n");
  
  sprintf(buf, "SUBSMPL = %d", input_str->subsmpl);
  strcat(input_str->input_parms, buf);
  strcat(input_str->input_parms, "\n");

  sprintf(buf, "EVAL = %d", input_str->evalmask);
  strcat(input_str->input_parms, buf);
  strcat(input_str->input_parms, "\n");

  return 0;
}




int msl2val_get_item(char *arg, inputstr *input_str)
{
  char *tmp_str; 
  char *tmp_prods, prods[4096];
  char *tmp_ptr;
  char keyword [20]; 
  char str_buf [20];
  char parm_str[4096];
  char tmp_file[FILENAME_MAX];
  int  i, iret, ilen1;
  FILE  *fp;
  int32 nprods;
  
    

  if ((tmp_str = strchr(arg, '=')) == NULL) {
    HY1C_out("Invalid argument %s\n", arg);
    return -1;
  }

  ilen1 = tmp_str-arg;
  strncpy(keyword, arg, ilen1);
  keyword[ilen1] = '\0';
  strcpy(parm_str, tmp_str+1);

  /* change keyword to lower case */
  tmp_str = keyword;
  while (*tmp_str != '\0') {
    if (isupper(*tmp_str)) *tmp_str = tolower(*tmp_str);
    tmp_str++;
  }

  if (strcmp(keyword, "par") == 0) {
    iret = msl2val_par_file(parm_str, input_str);
    return iret;
  
  } else if (strcmp(keyword, "il3file") == 0) {
    parse_file_name(parm_str, tmp_file);
    strcpy(input_str->il3file, tmp_file);

  } else if (strncmp(keyword, "il2file", 6) == 0) {

    for (i=0; i<MAX_IFILES; i++) {
      if (i<9)
        sprintf(str_buf, "il2file%1d", i+1);
      else if (i<99)
        sprintf(str_buf, "il2file%2d", i+1);
      else
        sprintf(str_buf, "il2file%3d", i+1);
 
      if (strcmp(keyword, str_buf) == 0) {
        parse_file_name(parm_str, tmp_file);
        strcpy((input_str->il2files)[i], tmp_file);
        break;
      }
    }
    if (i >= MAX_IFILES) goto Invalid_return;

  } else if (strcmp(keyword, "il2list") == 0) {

    parse_file_name(parm_str, tmp_file);
    if ((fp = fopen(tmp_file, "r")) == NULL) {
   	HY1C_out("-E- %s line %d: Unable to open input file list %s\n", __FILE__,__LINE__, tmp_file);
	goto Invalid_return;
    }
    tmp_file[0] = '\x0';
    for (i=0; i<MAX_IFILES; i++) {
      if (fscanf(fp, "%s\n", tmp_file) != EOF)
      	strcpy((input_str->il2files)[i], tmp_file);
      else
        break;
    }
    fclose(fp);

  } else if (strcmp(keyword, "ofile") == 0) {
    parse_file_name(parm_str, tmp_file);
    strcpy(input_str->ofile, tmp_file);

  } else if (strcmp(keyword, "l2prod") == 0) {
 
    /* Remove any " or ' left when reading parameter file */
    tmp_prods = parm_str;
    if (tmp_prods[0] == '"' || tmp_prods[0] == '\'') tmp_prods++;
    ilen1 = strlen(tmp_prods) - 1;
    if (tmp_prods[ilen1] == '"' || tmp_prods[ilen1] == '\'') tmp_prods[ilen1] = '\0';

    strcpy(prods, tmp_prods);
    i = 0;
    do {
	tmp_ptr = strchr((const char *)prods, ',');
	if (tmp_ptr != NULL) {
	    i++;
	    tmp_ptr++;
	    ilen1 = strlen(tmp_ptr);
	    memmove((void *)prods, (const void *)tmp_ptr, ilen1);
	    prods[ilen1] = '\x0';
	}
    } while (tmp_ptr != NULL);
    
    if ((strlen(tmp_prods) < 2) || (strlen(tmp_prods) > 10 && i == 0)) {
    
        HY1C_out("-E- %s line %d: No L2 products are given or the product list is not separated by commas\n", __FILE__,__LINE__);
	goto Invalid_return;
	
    } else {
    
	input_str->nl2prods = ++i;
    	iret = strproducts(tmp_prods, &nprods, (prname *)input_str->l2prods);
	if (input_str->nl2prods != nprods) {
      	     HY1C_out("-E- %s line %d: Wrong number of L2 products estimated and read\n", __FILE__,__LINE__);
	     goto Invalid_return;
  	}
   }	
    
  } else if (strcmp(keyword, "l3prod") == 0) {

    /* Remove any " or ' left when reading parameter file */
    tmp_prods = parm_str;
    if (tmp_prods[0] == '"' || tmp_prods[0] == '\'') tmp_prods++;
    ilen1 = strlen(tmp_prods) - 1;
    if (tmp_prods[ilen1] == '"' || tmp_prods[ilen1] == '\'') tmp_prods[ilen1] = '\0';

    strcpy(prods, tmp_prods);
    i = 0;
    do {
	tmp_ptr = strchr((const char *)prods, ',');
	if (tmp_ptr != NULL) {
	    i++;
	    tmp_ptr++;
	    ilen1 = strlen(tmp_ptr);
	    memmove((void *)prods, (const void *)tmp_ptr, ilen1);
	    prods[ilen1] = '\x0';
	}
    } while (tmp_ptr != NULL);
    
    if ((strlen(tmp_prods) < 2) || (strlen(tmp_prods) > 10 && i == 0)) {
    
        HY1C_out("-E- %s line %d: No l3 products are given or the product list is not separated by commas\n", __FILE__,__LINE__);
	goto Invalid_return;
	
    } else {
    
	input_str->nl3prods = ++i;
    	iret = strproducts(tmp_prods, &nprods, (prname *)input_str->l3prods);
	if (input_str->nl3prods != nprods) {
      	     HY1C_out("-E- %s line %d: Wrong number of l3 products estimated and read\n", __FILE__,__LINE__);
	     goto Invalid_return;
  	}
   }	
 
  } else if (strcmp(keyword, "l2flaguse") == 0) {
    parse_file_name(parm_str, tmp_file);
    strcpy(input_str->l2flaguse, tmp_file);
 
  } else if (strcmp(keyword, "l3flaguse") == 0) {
    parse_file_name(parm_str, tmp_file);
    strcpy(input_str->l3flaguse, tmp_file);
 
  } else if (strcmp(keyword, "deepwater") == 0) {
    input_str->deepwater = atoi(parm_str);
 
  } else if (strcmp(keyword, "bpass_corr") == 0) {
    input_str->bpass_corr = atoi(parm_str);
 
  } else if (strcmp(keyword, "subsmpl") == 0) {
    input_str->subsmpl = atoi(parm_str);

  } else if (strcmp(keyword, "eval") == 0) {
    input_str->evalmask = atoi(parm_str);

  } else {
    goto Invalid_return;
  }

  return 0;


Invalid_return:
  HY1C_out("Invalid argument \"%s\"\n", arg);
  return(FATAL_ERROR);
  
}




int msl2val_par_file(char *pfile, inputstr *input_str)
{
  FILE *fp;
  char arg[2048];
  int  iret;

  if ((fp = fopen(pfile, "r")) == NULL) {
    HY1C_out("Error on opening the parameter file - %s\n", pfile);
    return -1;
  }

  while ((fgets(arg, 2047, fp)) != NULL) {
    /* skip the comment or blank line */
    if (arg[0] == '#' || arg[0] == ';' || arg[0] == ' ' || arg[0] == '\0' ||
        arg[0] == '\n')   
      continue;

    arg[strlen(arg)-1] = '\0';    /* replace the last char new_line to NULL */
    iret = msl2val_get_item(arg, input_str);
    if (iret != 0) {
      fclose(fp);
      return -1;
    }
  }

  fclose(fp);
  return 0;
}




int msl2val_usage (char *progname)
{

  HY1C_out("%s %s (%s %s)\n",progname,VERSION,__DATE__,__TIME__);
  HY1C_out("\nUsage: \n\n");
  HY1C_out(" %s par=name_of_parameter_file\n", progname);
  HY1C_out("OR\n");
  HY1C_out(" The list of valid keywords:\n");
  HY1C_out("\n");
  HY1C_out("   il3file=input_l3_file_name - name of the input l3 daily/weekly\n");
  HY1C_out("   il2list=l2_file_list - file name containing a list of l2 files to match this time-period\n");
  HY1C_out("   alternately:\n");
  HY1C_out("   il2file1...il2filen=input_l2_file_names - names 1..n of l2 files to match this time-period\n");
  HY1C_out("   ofile=output-file_name - name of the output HDF pixel file\n");
  HY1C_out("   l2prod=a list of L2 products outputted to the HDF ofile, default:\n");
  HY1C_out("     l2prod=chlor_a,nLw_412,nLw_443,nLw_488,nLw_531,nLw_551,nLw_667,taua_869,epsilon,solz,sola,senz,sena,polcor_412,polcor_869,dpol_412,dpol_869,glint_coef,cloud_albedo,l2_flags,mside,detnum,pixnum\n");
  HY1C_out("   l3prod=a list of L3 products outputted to the HDF ofile, default:\n");
  HY1C_out("     l3prod=chlor_a,nLw_412,nLw_443,nLw_488,nLw_531,nLw_551,nLw_667,taua_869,epsilon\n");
  HY1C_out("   l2flaguse=flag_string - flags to be applied to l2 data\n");
  HY1C_out("   l3flaguse=flag_string - flags to be applied to l3 data\n");
  HY1C_out("   deepwater - selecting for deep water bins, default=1\n");
  HY1C_out("   bpass_corr - nLw wavelength adjustment (SeaWiFS to MODIS 6-bands only), default=0\n");
  HY1C_out("   subsmpl - valid pixel subsampling rate, default = 1\n");
  HY1C_out("   evalmask - evaluation switches, default=0\n");
  HY1C_out("\n");
  exit(FATAL_ERROR);

}




int32 locate(int32 *binlist_buf, int32 n_records, int32 bin)
{

   int32  jl = 0, ju = n_records-1, jm;
   
   while ( ju - jl > 1 ) {
   	jm = (ju + jl)/2;
	if ((binlist_buf[n_records-1] > binlist_buf[0]) == (bin > binlist_buf[jm]))
	     jl = jm;
	else
	     ju = jm;
   }
   if (jm >= n_records) return( -1 );
   if (binlist_buf[jm] == bin) return(jm);
   if (jm >= 1 && binlist_buf[jm-1] == bin) return(jm-1);
   if (jm+1 < n_records && binlist_buf[jm+1] == bin) return(jm+1);
   return( -1 );
   
}
	
  



int nLw_SeaWiFS2MODIS(int32_t sensorID, float *nLwbands)
{

  static int      firstCall = 1;
  static double   A0[7], A1[7], A2[7], A3[7];
  double  iratio, iratiosq, iratio3, s;
  int     i;
  /* Terra full bandpass from SeaWiFS nominal 11nm bands */
  double  AT0[7]={1.0203581, 0.9774594, 0.9170032, 0.9971813, 0.9056823, 1.0778310, 0.9905358};
  double  AT1[7]={-0.0086241, 0.0077102, 0.0483961, 0.0560854, 0.1494819, -0.1217537, -0.1196560};
  double  AT2[7]={-0.0000673, -0.0033085, -0.0098749, -0.0203777, -0.0156370, 0.0635462, 0.0570984};  
  double  AT3[7]={0.0001394, 0.0004185, 0.0009964, 0.0013208, -0.0001806, -0.0074462, -0.0066622};  
  /* Aqua full bandpass from SeaWiFS nominal 11nm bands  */
  double  AA0[7]={1.0165582, 0.9796995, 0.9213803, 1.0063582, 0.9100613, 1.0755772, 0.9958192};
  double  AA1[7]={-0.0084433, 0.0063936, 0.0436586, 0.0478614, 0.1439513, -0.1206972, -0.1294248};
  double  AA2[7]={-0.0002650, -0.0028901, -0.0091218, -0.0186584, -0.0145033, 0.0631593, 0.0594573};
  double  AA3[7]={0.0001779, 0.0003677, 0.0009270, 0.0011990, -0.0002594, -0.0074087, -0.0068372};
  
  
  /* load parameters */
  if (firstCall) {
  
      if (sensorID == MODIST)
      
      	for (i=0; i<7; i++) { 
	     A0[i] = AT0[i];
      	     A1[i] = AT1[i];
      	     A2[i] = AT2[i];
      	     A3[i] = AT3[i];
        }
	
      else if (sensorID == MODISA)
  
      	for (i=0; i<7; i++) {  
	     A0[i] = AA0[i];
      	     A1[i] = AA1[i];
      	     A2[i] = AA2[i];
      	     A3[i] = AA3[i];
        }
      else {
      
        HY1C_out("nLw_SeaWiFS2MODIS: Error: Needs to be MODISA or MODIST for band pass conversion\n");
        return(1);
      }
	
      firstCall = 0;
       
  }


  if (*(nLwbands+2) <= 0.0 || *(nLwbands+4) <= 0.00001) return(1);
  iratio   = (double) *(nLwbands+2);
  iratio  /= (double) *(nLwbands+4);
  if (iratio > 5.15) return(1);
  iratiosq = iratio*iratio;
  iratio3  = iratiosq*iratio;
  
  for (i=0; i<NBANDSVIS; i++) {
  
	s = A0[i]+A1[i]*iratio+A2[i]*iratiosq+A3[i]*iratio3;
	if (i == 3) *(nLwbands+i) = (0.56*(*(nLwbands+i))+0.44*(*(nLwbands+i+1))) * s; else *(nLwbands+i) *= s;
	if (*(nLwbands+i) < 0.0) *(nLwbands+i) = 0.0;

  }

  return(0);
    
}  







void meanday(int32_t year1, int32_t day1, int32_t msec1, int32_t year2, int32_t day2, int32_t msec2, int32_t *myear, int32_t *mday, int32_t *mmsec)
{

  int32_t i;
  int32_t day;
  uint64_t msecs=0, daylong=86400000;


  if (year2 > year1) {
     if (isleap(year1) == TRUE) day = 366-day1; else day = 365-day1;
     for (i=year1+1; i<year2; i++)
    	if (isleap(i) == TRUE) day += 366; else day += 365;
     day += day2;
     msecs = daylong-(uint64_t)msec1;
     for (i=0; i<day-1; i++) msecs += daylong;
     msecs += (uint64_t)msec2;
     msecs /= 2;
     *mmsec = (int32_t)((msec1 + msecs) % daylong);
     *mday = day1 + (int32_t)(msec1 + msecs) / daylong;
     *myear = year1;
     for (i=year1+1; i<=year2; i++) {
	if (isleap(i) == TRUE) if (*mday <= 366) break; else {
	     *mday -= 366; 
	     *myear = i;
	} else if (*mday <= 365) break; else {
	     *mday -= 365; 
	     *myear = i;
	}
     }
  } else if (year1 > year2) {
     if (isleap(year2) == TRUE) day = 366-day2; else day = 365-day2;
     for (i=year2+1; i<year1; i++)
    	if (isleap(i) == TRUE) day += 366; else day += 365;
     day += day1;
     msecs = daylong-(uint64_t)msec2;
     for (i=0; i<day-1; i++) msecs += daylong;
     msecs += (uint64_t)msec1;
     msecs /= 2;
     *mmsec = (int32_t)((msec2 + msecs) % daylong);
     *mday = day2 + (int32_t)(msec2 + msecs) / daylong;
     *myear = year2;
     for (i=year2+1; i<=year1; i++) {
	if (isleap(i) == TRUE) if (*mday <= 366) break; else {
	     *mday -= 366; 
	     *myear = i;
	} else if (*mday <= 365) break; else {
	     *mday -= 365; 
	     *myear = i;
	}
     }
  } else {
     day = labs(day1 - day2);
     if (day1 > day2) {
         msecs = daylong-(uint64_t)msec2;
     	 for (i=0; i<day-1; i++) msecs += daylong;
     	 msecs += (uint64_t)msec1;
	 msecs /= 2;
	 *mmsec = (int32_t)((msec2 + msecs) % daylong);
	 *mday = day2 + (int32_t)(msec2 + msecs) / daylong;
	 *myear = year2;
     }
     else if (day2 > day1) {
     	 msecs = daylong-(uint64_t)msec1;
     	 for (i=0; i<day-1; i++) msecs += daylong;
     	 msecs += (uint64_t)msec2;
	 msecs /= 2;
	 *mmsec = (int32_t)((msec1 + msecs) % daylong);
	 *mday = day1 + (int32_t)(msec1 + msecs) / daylong;
	 *myear = year1;
     } 
     else {
     	 msecs = (uint64_t)labs(msec1 - msec2);
	 msecs /= 2;
	 if (msec1 > msec2) *mmsec = msec2 + msecs; else *mmsec = msec1 + msecs;
	 *mday = day1;
	 *myear = year1;
     }
  }
  
}



int32 find_L2L3products(inputstr input, msvalstr *valstr)
{
   int32_t i;
   int32_t nbands, *Lambda_p;
   
   
   nbands = rdsensorinfo(valstr->l2sensorID, input.evalmask, NULL, NULL);
   valstr->nl2bands = nbands;
   valstr->nl2prods = input.nl2prods;
   
   if ((valstr->l2prods = (prname *)malloc(valstr->nl2prods*sizeof(prname))) == NULL) {
        HY1C_out("-E- : Error allocating memory to L2 product names.\n");
        exit(FATAL_ERROR);
   }
   for (i=0; i<valstr->nl2prods; i++) strcpy(valstr->l2prods[i], input.l2prods[i]);
   
   if ((valstr->l2Lambda = (int32_t *)malloc(nbands*sizeof(int32_t))) == NULL) {
        HY1C_out("-E- : Error allocating memory to Lambda vector.\n");
        exit(FATAL_ERROR);
   }
   
   i = rdsensorinfo(valstr->l2sensorID, input.evalmask, "Lambda", (void **) &Lambda_p);
   
   for (i=0; i<valstr->nl2bands; i++) valstr->l2Lambda[i] = Lambda_p[i];
     
   
   if (valstr->l3sensorID != valstr->l2sensorID) nbands = rdsensorinfo(valstr->l3sensorID, input.evalmask, NULL, NULL);
   valstr->nl3bands = nbands;
   valstr->nl3prods = input.nl3prods;
   
   if ((valstr->l3prods = (prname *)malloc(valstr->nl3prods*sizeof(prname))) == NULL) {
        HY1C_out("-E- : Error allocating memory to L3 product names.\n");
        exit(FATAL_ERROR);
   }
   for (i=0; i<valstr->nl3prods; i++) strcpy(valstr->l3prods[i], input.l3prods[i]);
	
   
   if ((valstr->l3Lambda = (int32_t *)malloc(nbands*sizeof(int32_t))) == NULL) {
        HY1C_out("-E- : Error allocating memory to Lambda vector.\n");
        exit(FATAL_ERROR);
   }
   
   if (valstr->l3sensorID != valstr->l2sensorID) {
      
   	i = rdsensorinfo(valstr->l3sensorID, input.evalmask, "Lambda", (void **) &Lambda_p);
   	for (i=0; i<valstr->nl3bands; i++) valstr->l3Lambda[i] = Lambda_p[i];

   } else {
        for (i=0; i<nbands; i++) valstr->l3Lambda[i] = valstr->l2Lambda[i];
   }
   
   i = strlen(input.input_parms);
   if ((valstr->input_parms = (char *)malloc((i+1)*sizeof(char))) == NULL) {
        HY1C_out("-E- : Error allocating memory to input parameter string.\n");
        exit(FATAL_ERROR);
   }
   
   strncpy(valstr->input_parms, input.input_parms, i);
   valstr->input_parms[i] = '\x0';
   
   
   return SUCCEED;
     
}




int32 strproducts(char *prodstr, int32 *nprods, prname *l2prods)
{

    int i, j, np;
    char  str[2000], *ptr, prod[PRODSTRLEN];
    

    strcpy(str, prodstr);
    np = strlen(str);
    str[np] = '\x0';  
    
    j = 0;
    for (i=0; i<np; i++) {
    	if (str[i] == '#' || str[i] == ';' || str[i] == ':') str[j++] = ','; 
	else
    	if (str[i] == ' ') continue; 
	else
    	str[j++] = str[i];
    }
    str[j] = '\x0'; 
    
    
    ptr = strchr((const char *)str, ',');
    if (strlen(str) > 15 && ptr == NULL) {
        HY1C_out("-E- %s line %d: The product names in the product list are not separated by commas\n", __FILE__,__LINE__);
        exit(FATAL_ERROR);
    }
    
    i = -1;
    np = 0;
    do {
    
       i++;
       j = 0;
       do {
       	  prod[j++] = str[i++];
       } while (str[i] != '\x0' && str[i] != ',');
       
       prod[j] = '\x0';
       strcpy(l2prods[np++], prod);
       
    } while (str[i] != '\x0');
    
    *nprods = np;
   
    return SUCCEED;
     
}
    
    
    
    
    
    

